<?php


namespace App\HttpController\Api;


use App\Common\AuthService;
use EasySwoole\EasySwoole\Config;
use EasySwoole\EasySwoole\Logger;
use EasySwoole\EasySwoole\Trigger;
use EasySwoole\Http\AbstractInterface\Controller;
use EasySwoole\Http\Message\Status;
use EasySwoole\Jwt\Jwt;
use EasySwoole\Validate\Validate;


abstract class Base extends Controller
{
    /**
     * @var array
     * 忽略不需要验证的路由地址
     */
    private $basicAction = [
        '/api/system/user/login',
    ];
    // 请求token
    protected $token;
    // 用户详细信息
    protected $userInfo;
    /**
     * @var int 页码
     */
    protected $page = 1;
    /**
     * @var int 显示条数
     */
    protected $limit = 10;

    /**
     * @var array 搜索条件
     */
    protected $search = [];

    /**
     * @var int 用户id
     */
    protected $userId;

    /**
     * @var array 请求参数
     */
    protected $requestData = [];


    public function index()
    {
        // 没有找到改方法继续在子方法找
        $this->actionNotFound('index');
    }

    protected function onRequest(?string $action): ?bool
    {
        //父类返回是false 这里就返回false
        if (!parent::onRequest($action)) {
            return false;
        }
        // 获取当前请求的地址
        $path = $this->request()->getUri()->getPath();
        //判断是否不需要验证
        if (!in_array($path, $this->basicAction)) { //需要验证进来
//             必须带有有token
            $token = $this->request()->getHeader('token');
            $token = empty($token) ? $token : $token[0];
            if (empty($token)) {
                $this->writeJson(new \stdClass(), "token不可为空", Status::CODE_BAD_REQUEST);
                return false;
            }
//         验证JWT token 是否合法
            if (!$this->verifyToken($token)) {
                return false;
            }
            // 权限策略判断
            if (!AuthService::getInstance()->checkNode($path, $this->userId)) {
                $this->writeJson(new \stdClass(), "无权限访问该接口", Status::CODE_BAD_REQUEST);
                return false;
            }
        }

        // 各个action的参数校验
        $v = $this->getValidateRule($action);
        if ($v && !$this->validate($v)) {
            $this->writeJson(new \stdClass(), $v->getError()->__toString(), Status::CODE_BAD_REQUEST);
            return false;
        }

        $this->setRequestData(); //统一接受数据
        $this->setPage(); //设置分页数据
        return true;
    }

//    protected function afterAction(?string $actionName): void
//    {
//        parent::afterAction($actionName); // TODO: Change the autogenerated stub
//    }

    //
    private function setRequestData()
    {
        $data = $this->request()->getRequestParam();
        $this->requestData = $data;
        return true;
    }

    // 设置分页数据
    private function setPage()
    {
        $page = $this->request()->getParsedBody('page');
        $limit = $this->request()->getParsedBody('limit');
        $search = $this->request()->getParsedBody('search');
        if (!empty($page) && $page >= 1) $this->page = $page;
        if (!empty($limit) && $limit >= 10) $this->limit = $limit;
        if (!empty($search) && json_encode($search)) $this->search = json_decode($search, true);
        return true;
    }


    /**
     * @param int $statusCode
     * @param null $result
     * @param string $msg
     * @return bool
     * 返回的json 数据重写
     */
    public function writeJson($result = NULL, $msg = 'OK', $statusCode = Status::CODE_OK)
    {
        if (!$this->response()->isEndResponse()) {
            $data = Array(
                "code" => $statusCode, //状态码
                "result" => $result,  //结果
                "msg" => $msg  //消息
            );
            $this->response()->write(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            $this->response()->withHeader('Content-type', 'application/json;charset=utf-8');
            $this->response()->withStatus(200);
            return true;
        } else {
            return false;
        }
    }


    public function success($msg)
    {
        return [
            'code' => 1,
            'msg' => $msg
        ];
    }

    public function error($msg)
    {
        return [
            'code' => 0,
            'msg' => $msg
        ];
    }


    /**
     * @param $token
     * @return bool
     * 验证token是否合法
     */
    private function verifyToken($token)
    {
        $jwtConf = Config::getInstance()->getConf('JWT');
        try {
            //解密
            $result = Jwt::getInstance()->setSecretKey($jwtConf['key'])->decode($token);
            $msg = '';
            switch ($result->getStatus()) {
                case -1:
                    $msg = 'token签名验证无效';
                    break;
                case  -2:
                    $msg = 'token过期';
                    break;
            }
            if ($msg) {
                $this->writeJson(new \stdClass(), $msg, Status::CODE_UNAUTHORIZED);
                return false;
            }
            $this->userInfo = $result->getData();
            $this->userId = $result->getData()['user_id'];
            // 根据解密之后的结果完善业务逻辑
            return true;
        } catch (\Exception $e) {    // TODO: 处理异常
            $this->writeJson(new \stdClass(), 'token异常', Status::CODE_UNAUTHORIZED);
            return false;
        }

    }


    /**
     * @param \Throwable $throwable
     * 系统错误异常捕获处理
     */
    protected function onException(\Throwable $throwable): void
    {
        go(function () use ($throwable) {
            Trigger::getInstance()->throwable($throwable);
            $msg = $throwable->getFile() . ' line: ' . $throwable->getLine() . ' msg: ' . $throwable->getMessage();
            Logger::getInstance()->log($msg, Logger::LOG_LEVEL_ERROR, 'DEBUG');
        });
        //直接给前端响应500并输出系统繁忙
        $this->writeJson(new \stdClass(), '系统繁忙,请稍后再试'.$throwable->getMessage(), Status::CODE_INTERNAL_SERVER_ERROR);

    }

    /**
     * @param string|null $action
     * @return Validate|null
     * 接口参数认证以及检查
     */

    abstract protected function getValidateRule(?string $action): ?Validate;

    /**
     * @param \Throwable $exception
     * 手动记录错误日志
     */
    public function errorLog(\Throwable $exception)
    {
        go(function () use ($exception) {
            $msg = $exception->getFile() . ' line: ' . $exception->getLine() . ' msg: ' . $exception->getMessage();
            Logger::getInstance()->log($msg, Logger::LOG_LEVEL_ERROR, 'DEBUG');
        });
    }


}
